﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Vortex {

	/// <summary>
	/// State complete handler. Argument - state which has been completed.
	/// </summary>
	public delegate void StateCompleteHandler<T>(T completedState);

	/// <summary>
	/// Simple state controller. It implements state holder and allows to apply state for a some time.
	/// </summary>
	/// <typeparam name="T">State type. It can be enum or event class instance with rich content.</typeparam>
	public class StateController<T> : IStateHolder<T>, IUpdatable {
		private T _state;
		private StateCompleteHandler<T> _completeCallback;
		private float _timeEllapsed;
		private float _stateDuration;
		private float _progress;

		/// <summary>
		/// Initializes a new instance of the <see cref="StateController&lt;T&gt;"/> class.
		/// </summary>
		/// <param name="state">The initial state.</param>
		public StateController(T state) {
			State = state;
		}

		#region IStateHolder<T> Members

		/// <summary>
		/// Gets or sets the state.
		/// </summary>
		/// <value>The state.</value>
		public T State {
			get { return _state; }
			set {
				SwitchState(value);
			}
		}

		#endregion

		/// <summary>
		/// Gets the progress of time limited state.
		/// </summary>
		/// <value>The progress of time limited state in range 0..1.</value>
		public float Progress {
			get { return _progress; }
		}

		/// <summary>
		/// Gets the duration of the state. For unrestricted time states this value is zero.
		/// </summary>
		/// <value>The duration of the state. Zero if state has no time limit.</value>
		public float StateDuration {
			get { return _stateDuration; }
		}

		/// <summary>
		/// Gets the time ellapsed since state was set.
		/// </summary>
		/// <value>The time ellapsed for current state.</value>
		public float TimeEllapsed {
			get { return _timeEllapsed; }
		}

		/// <summary>
		/// Gets the time left for time limited state
		/// </summary>
		/// <value>The time left for time limited state.</value>
		public float TimeLeft {
			get { return _stateDuration - _timeEllapsed; }
		}

		/// <summary>
		/// Switches the state time unlimited.
		/// </summary>
		/// <param name="newState">The new state.</param>
		public void SwitchState(T newState) {
			_state = newState;
			_timeEllapsed= _stateDuration = _progress = 0f;
		}

		/// <summary>
		/// Switches the state with time limited
		/// </summary>
		/// <param name="newState">The new state.</param>
		/// <param name="stateDuration">Duration of the state in seconds.</param>
		/// <param name="completeCallback">The state complete callback.</param>
		public void SwitchState(T newState, float stateDuration, StateCompleteHandler<T> completeCallback) {
			if (null == completeCallback) throw new ArgumentNullException("switchCallback");
			if (stateDuration <= 0) throw new ArgumentException("State duration should be greater than zero", "stateDuration");

			_state = newState;
			_completeCallback = completeCallback;
			_stateDuration = stateDuration;
			_timeEllapsed = _progress = 0f;
		}

		/// <summary>
		/// Switches the state.
		/// </summary>
		/// <param name="newState">The new state.</param>
		/// <param name="stateDuration">Duration of the state.</param>
		/// <param name="restoreState">State of the restore.</param>
		public void SwitchState(T newState, float stateDuration, T restoreState) {
			SwitchState(newState, stateDuration, (T t) => SwitchState(restoreState) );
		}


		#region IUpdatable Members

		/// <summary>
		/// Updates the specified controller with frame time delta.
		/// </summary>
		/// <param name="timeDelta">The time delta in seconds.</param>
		public void Update(float timeDelta) {
			//increases ellapsed time
			_timeEllapsed += timeDelta;

			if (_stateDuration > 0) {
				_progress = Math.Min(_timeEllapsed / _stateDuration, 1);
				if (_timeEllapsed >= _stateDuration) {
					//reset all of parameters
					_stateDuration = 0;
					_timeEllapsed = 0;
					//invoke callback
					_completeCallback(_state);
					
				}
			}
		}

		#endregion
	}
}
